﻿using System;
using System.Collections.Generic;
using System.Linq;
using collision_and_rigid;
using game_config;
using game_stuff;

namespace rogue_game
{
    public class BattleNpc
    {
        private BattleNpc(CharacterBody characterBody, WantedBonus wantedBonus, int npcId)
        {
            CharacterBody = characterBody;
            WantedBonus = wantedBonus;
            NpcId = npcId;
        }

        public int NpcId { get; }
        public CharacterBody CharacterBody { get; }
        public WantedBonus WantedBonus { get; }


        public static BattleNpc GenById(int id, int gid, int team, Random random,
            int nowChapterExtraPassiveNum, int weaponLv, RogueFixData? fixData, out int botId)
        {
            var genByConfig = CommonConfig.Configs.battle_npcs.TryGetValue(id, out var battleNpc)
                ? GenByConfig(battleNpc, gid, team, random, nowChapterExtraPassiveNum, weaponLv, fixData)
                : throw new KeyNotFoundException();
            botId = battleNpc.botId;
            return genByConfig;
        }

        private static BattleNpc GenByConfig(battle_npc battleNpc, int gid, int team, Random random,
            int nowChapterExtraPassiveNum, int weaponLv, RogueFixData? fixData)
        {
            var b1 = fixData != null;
            if (b1)
            {
                b1 = fixData!.Weapons != null && fixData!.Weapons.Any() &&
                     fixData.Weapons.All(x => x > 0 && CommonConfig.Configs.weapons.ContainsKey((weapon_id)x));
            }

            var select =
                b1
                    ? fixData!.Weapons!.Select(x =>
                    {
                        //todo AI use bow and spear 
                        var weaponId = (weapon_id)x;
                        if (weaponId == weapon_id.test_bow || weaponId == weapon_id.test_spear)
                        {
                            weaponId = weapon_id.test_sword;
                        }

                        return weaponId;
                    }).ToList()
                    : battleNpc.Weapons.Select(x => Weapon.GenId(x.ChooseRandOne())).ToList();


            var characterInitData =
                CharacterInitData.GenNpcByConfig(gid, team, select, battleNpc.BodyId, battleNpc.AttrId,
                    battleNpc.MaxWeaponSlot);
            var all = fixData is { PassiveToLv: { } } && fixData.PassiveToLv.Any();
            if (all)
            {
                if (fixData!.PassiveToLv!.Select(keyValuePair => (passive_id)keyValuePair.Key)
                    .All(passiveId => CommonConfig.Configs.passives.ContainsKey(passiveId)))
                {
                    all = true;
                }
            }


            var battleNpcPassiveRange = all
                ? GenPassiveRange(fixData!.PassiveToLv!)
                : battleNpc.PassiveRange
                    .Select(x => x.TryStringToEnum<passive_id>(out var pid) ? pid : throw new Exception()).ToArray();

            if (all)
            {
                var aggregate = battleNpcPassiveRange.Aggregate("", (s, x) => s + "," + x);
                Console.Out.WriteLine($"npc boss passive Range is {aggregate}");
            }

            if (!battleNpcPassiveRange.Any())
            {
                battleNpcPassiveRange = battleNpc.PassiveRange
                    .Select(x => x.TryStringToEnum<passive_id>(out var pid) ? pid : throw new Exception()).ToArray();
            }

            var chooseRandCanSame =
                battleNpcPassiveRange.ChooseRandCanSame(battleNpc.PassiveNum + nowChapterExtraPassiveNum, random);

            var selectMany = chooseRandCanSame.SelectMany(x => PassiveTrait.GenManyByPId(x, 1));
            var enumerable = battleNpc.MustPassive.SelectMany(x => PassiveTrait.GenManyById(x.pass, x.level));
            var traits = selectMany.Union(enumerable);

            var passiveTraits = traits.GroupBy(x => x.PassId)
                .ToDictionary(x => x.Key, x =>
                {
                    var passiveTrait = x.First();
                    var sum = x.Sum(pp => pp.Level);
                    passiveTrait.SetLevel((uint)sum);
                    return passiveTrait;
                });

            var genCharacterBody = characterInitData.GenCharacterBody(TwoDPoint.Zero(), passiveTraits);
            var characterStatus = genCharacterBody.CharacterStatus;
            foreach (var fix in CommonConfig.Configs.weapon_unlocks.Values.Select(PlayBuffFixEffect.GenByConfig))
            {
                characterStatus.PickABuffFixEffect(fix, (uint)weaponLv);
            }

            characterStatus.FullAmmo();
            characterStatus.SetPropPoint(battleNpc.PropPoint);

            var battleNpcWithVehicleId = battleNpc.WithVehicleId;
            if (battleNpcWithVehicleId != "")
            {
                var genById = Vehicle.GenById(battleNpcWithVehicleId);

                characterStatus.GetInAVehicle(genById);
            }

            var battleNpcPropIds = battleNpc.PropIds;
            if (battleNpcPropIds.Any())
            {
                var chooseRandOne = b1 && fixData!.Prop > 0 &&
                                    CommonConfig.Configs.props.ContainsKey((prop_id)fixData!.Prop)
                    ? (prop_id)fixData.Prop
                    : battleNpcPropIds.ChooseRandOne(random).TryStringToEnum<prop_id>(out var ppid)
                        ? ppid
                        : throw new Exception();
                var byId = Prop.GenById(chooseRandOne);
                characterStatus.PickAProp(byId);
            }

            static ICanPutInMapInteractable? Func(interactableType t, string id)
            {
                return t switch
                {
                    interactableType.weapon => Weapon.GenById(id),
                    interactableType.prop => Prop.GenById(id),
                    interactableType.vehicle => Vehicle.GenById(id),
                    _ => null
                };
            }

            var gameItems = battleNpc.KillDrops.Select(GameItem.GenByConfigGain).ToArray();
            var array = battleNpc.AllDrops.Select(GameItem.GenByConfigGain).ToArray();
            var randOne = battleNpc.DropMapInteractableId.ChooseRandOne();
            var canPutInMapInteractable =
                Func(battleNpc.DropMapInteractableType, randOne);
            var mapInteractable = canPutInMapInteractable?.PutInteractable(TwoDPoint.Zero(), true);

            var wantedBonus = new WantedBonus(array, gameItems, mapInteractable);
            var genByConfig = new BattleNpc(genCharacterBody, wantedBonus, battleNpc.id);
            var npcBotId = battleNpc.botId;


            var b = npcBotId == 0;
            if (b)
            {
                throw new Exception($"no good bot id in {battleNpc.id}");
            }

            return genByConfig;
        }

        private static passive_id[] GenPassiveRange(Dictionary<int, int> passiveToLv)
        {
            var passiveIds = new List<passive_id>();
            foreach (var keyValuePair in passiveToLv)
            {
                var key = (passive_id)keyValuePair.Key;
                passiveIds.AddRange(Enumerable.Range(0, keyValuePair.Value).Select(x => key));
            }

            return passiveIds.ToArray();
        }
    }
}